/*************************************
 *  auth : sunqiang
 *  date : 20170112
 *  desp : memery pool manage
 *
 *************************************/

#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdint.h>

#include "sn_mempool.h"

sn_mempool_t *sn_create_mempool(int16_t begin, int16_t advance) {
	int16_t       i;
	int16_t       count;
	int16_t       real_begin;
	int32_t       size;
	sn_mempool_t  *mpool;
	sn_memblock_t *block;

	mpool = calloc(1, sizeof(sn_mempool_t));
	if (NULL == mpool) {
		return NULL;
	}

	real_begin   = alignment(begin, ALIGN_SIZE);
	mpool->begin = real_begin;

	for (i=0; i<MAX_ZORE_SIZE; i++) {
		if (i < advance) {
			count = 1;
			size  = sn_calc_allocblock_size(real_begin, i, 0);
			block = sn_create_memblock(&mpool->zore[i], size);
			if(NULL == block) {
				return NULL;
			}
			//printf("block%d add[%p].size[%d]\n", i, block, size);
			//printf("	zore[%p].\n", block->zore);
		} else {
			count = 0;
			block = NULL;
		}
		pthread_mutex_init(&mpool->zore[i].mutex,NULL);
		mpool->zore[i].count = count;
		mpool->zore[i].alloc = block;
	}

	return mpool;
}

void sn_destory_mempool(sn_mempool_t *mpool) {
	int16_t       i;
	sn_memblock_t *alloc;
	sn_memblock_t *mfree;
	sn_memblock_t *block;

	for (i=0; i<MAX_ZORE_SIZE; i++) {

		alloc = mpool->zore[i].alloc;
		mfree = alloc->last;
		while (mfree) {
			block = mfree->last;
			free(mfree);
			mfree = block;
		}

		mfree = alloc->next;
		while (mfree) {
			block = mfree->next;
			free(mfree);
			mfree = block;
		}

		free(alloc);
	}

	free(mpool);
	mpool = NULL;

	return;
}

void *sn_mpalloc(sn_mempool_t *mpool, int32_t size) {
	int16_t       site;
	int32_t       real_size;
	int32_t       block_size;
	sn_memblock_t *alloc;
	sn_memblock_t *block;

	real_size = alignment(size, ALIGN_SIZE);
	site = real_size / mpool->begin;
	site = site<(MAX_ZORE_SIZE-1)?site:(MAX_ZORE_SIZE-1);
	real_size += sizeof(sn_memblock_t **);

	alloc = mpool->zore[site].alloc;
	if (NULL == alloc || real_size > (alloc->tail-alloc->puse)) {
		pthread_mutex_lock(&mpool->zore[site].mutex);
		if (NULL != alloc && NULL != alloc->next) {
			block = alloc->next;
		} else {
			block_size = sn_calc_allocblock_size(mpool->begin, site, size);
			block = sn_create_memblock(&mpool->zore[site], block_size);
			if(NULL == block) {
				return NULL;
			}
			mpool->zore[site].count++;
			if (NULL != alloc) {
				mpool->zore[site].alloc->next = block;
			}
		}

		mpool->zore[site].alloc = block;
		pthread_mutex_unlock(&mpool->zore[site].mutex);

		alloc = block;
	}
	return sn_mballoc(alloc, real_size);
}

void sn_mpfree(void *mdata) {
	int32_t         head_size;
	sn_memblock_t   *blast;
	sn_memblock_t   *bnext;
	sn_memblock_t   *block;
	sn_memblock_t   *alloc;
	sn_memzore_t    *zore;

#if 1
	block = *((void**)(mdata-sizeof(void *)));
	zore  = block->zore;
	alloc = zore->alloc;
#else
	void **p;
	p = mdata-sizeof(void *);
	block = *p;
	printf("p[%p].mdata[%p]\n", p,mdata);
	printf("block[%p]\n", block);
	zore  = block->zore;
	alloc = zore->alloc;
#endif


	block->cited--;
	if (0 == block->cited && alloc != block) {
		blast = block->last;
		bnext = block->next;

		if (NULL != blast) {
			pthread_mutex_lock(&blast->mutex);
			blast->next = bnext;
			pthread_mutex_unlock(&blast->mutex);
		}
		if (NULL != bnext) {
			pthread_mutex_lock(&bnext->mutex);
			bnext->last = blast;
			pthread_mutex_unlock(&bnext->mutex);
		}

		bnext = alloc->next;

		pthread_mutex_lock(&block->mutex);
		block->last = alloc;
		block->next = bnext;
		pthread_mutex_lock(&block->mutex);

		pthread_mutex_lock(&bnext->mutex);
		bnext->last = block;
		pthread_mutex_lock(&bnext->mutex);

		pthread_mutex_lock(&alloc->mutex);
		bnext = block;
		pthread_mutex_lock(&alloc->mutex);
	} else if(0 == block->cited) {
		pthread_mutex_lock(&block->mutex);
		head_size = alignment(sizeof(sn_memblock_t), ALIGN_SIZE);
		block->puse = (void *)block+head_size;
		pthread_mutex_unlock(&block->mutex);
	}
	mdata = NULL;
}

sn_memblock_t * sn_create_memblock(sn_memzore_t *zore, int32_t size) {
	int           head_size;
	sn_memblock_t *block;

	head_size = alignment(sizeof(sn_memblock_t), ALIGN_SIZE);

	block = calloc(1, head_size + size);
	if (NULL == block) {
		return NULL;
	}

	pthread_mutex_init(&block->mutex,NULL);
	block->zore = zore;
	block->last = zore->alloc;
	block->next = NULL;
	block->puse = (void *)block+head_size;
	block->tail = block->puse+size;
	block->cited= 0;

	return block;
}

void *sn_mballoc(sn_memblock_t *block, int32_t size) {
#if 1
	void  **ptr;
	void  *data;

	pthread_mutex_lock(&block->mutex);
	ptr = block->puse;
	data = block->puse + sizeof(void *);
	*ptr = block;
	block->puse += size;
	block->cited++;
	pthread_mutex_unlock(&block->mutex);
#else
	void  *data;
	pthread_mutex_lock(&block->mutex);
	memcpy(block->puse, &block, sizeof(void *));
	data = block->puse + sizeof(void *);
	block->puse += size;
	block->cited++;
	pthread_mutex_unlock(&block->mutex);
#endif

	return data;
}

int32_t sn_calc_allocblock_size(int16_t begin, int16_t site, int32_t alloc) {
	int32_t  size;
	int32_t  nalloc;

	nalloc = alloc * ADVANCE_ALLOC_MOM;
	size = begin*(site+1)*ADVANCE_ALLOC_MOM;
	size = size>POOL_BLOCK_SIZE?size:POOL_BLOCK_SIZE;
	size = size>nalloc?size:nalloc;

	return size;
}
